#include "Classes/ChessMen/Bishop.h"
#include "Classes/ChessMan.h"
#include "Classes/ChessTable.h"
#include "Classes/ScreenOperations.h"
#include "Classes/Game.h"
#include "Classes/BmpLoader/imageloader.h"
#include "Classes/SpeechFile.h"
#include "Artificial/Tree.h"
#include "Artificial/AIChessTable.h"
#include "Classes/SoundSource.h"

#include <GL/gl.h>	// Header File For The OpenGL32 Library
#include <GL/glu.h>	// Header File For The GLu32 Library
#include <GL/glut.h>
#include <unistd.h> // Header File For sleeping.
#include <fstream>
#include <stdlib.h>
#include <string>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <AL/alut.h>
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alure.h>

using namespace std;
// gerekliymis list vector icin

/*	Create checkerboard texture	*/
#define	checkImageWidth 32
#define	checkImageHeight 32
#define ESCAPE 27
static GLubyte checkImage[checkImageHeight][checkImageWidth][4];
static GLuint texName;

ScreenOperations so;
ChessTable ct;
Game game;
SpeechFile sf;
SoundSource sndSource;
AIChessTable aiCt;

bool isFirstDisplay = true;

void init() {

	glClearColor(0.0, 0.2, 0.1, 0.0); // arkaplan rengi
	glShadeModel(GL_SMOOTH);
	glDepthFunc(GL_LESS); // The Type Of Depth Test To Do
	glEnable(GL_DEPTH_TEST); // Enables Depth Testing

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glGenTextures(1, &texName);
	glBindTexture(GL_TEXTURE_2D, texName);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	//	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	//	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

	//	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	//	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	Image* image = loadBMP("src/ImagesBMP/Table.bmp");
	glTexImage2D(GL_TEXTURE_2D, //Always GL_TEXTURE_2D
			0, //0 for now
			GL_RGB, //Format OpenGL uses for image
			image->width, image->height, //Width and height
			0, //The border of the image
			GL_RGB, //GL_RGB, because pixels are stored in RGB format
			GL_UNSIGNED_BYTE, //GL_UNSIGNED_BYTE, because pixels are stored
			//as unsigned numbers
			image->pixels); //The actual pixel data
	//Returns the id of the texture
}

void display(void) {

	glLoadIdentity();
	glTranslatef(0.0, 0.0, -3.2); // sinan // eskisi -2.7
	//glTranslatef(0.0, 0.0, -2.04); // ceren

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glRotatef(-38.0f, 1.0f, 0.0f, 0.0f); // sinan // eskisi -43.0
	//glRotatef(-43.9f, 0.0f, 0.0f, 1.0f); // ceren

	if (isFirstDisplay) //eger ilk gösterimse taslar oturtulacak, degilse normal devam edecek.
	{
		ct.InitializeChessTableMatrixWithNullChessMen(); // herseyi ilklendirir ve bosa alır tüm tahtayı
		ct.FillChessTableMatrixForBeginning(); // ilk durumu sağlar taşları yerine koyar.
		game.Initialize(); // oyuncular ayarlanır.
		game.Start(); // sıra beyaz a verilir
		isFirstDisplay = false;
		ChessTable::selectedCoordinate.x = -100; // ilk basta secilecek olan indexler farklı rakamlara atanır.
		ChessTable::selectedCoordinate.y = -100;
		sf.speechSituation = SpeechSituation_WaitingForSelect;
		ct.EqualTwoArrays(ChessTable::ChessTableMatrix, ChessTable::UndoMatrix);
		// acılısta hak beyaza verilir ve select istenir.
	}

	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
	glBindTexture(GL_TEXTURE_2D, texName);

	glPushMatrix();
	glTranslatef(-1.5, 1.2f, 0.0f); // once baslangıc noktası yukarıya alınır.
	glBegin(GL_QUADS); // Ortadaki tahta

	glTexCoord2f(0.0, 0.0);
	glVertex3f(0.0, 0.0, 0.0); //Orta
	glTexCoord2f(1.0, 0.0);
	glVertex3f(3.0, 0.0, 0.0);
	glTexCoord2f(1.0, -1.0);
	glVertex3f(3.0, -2.4, 0.0);
	glTexCoord2f(0.0, -1.0);
	glVertex3f(-0.0, -2.4, 0.0);

	glTexCoord2f(0.0, 0.0);
	glVertex3f(3.0, -2.4, 0.0); //orta Alt
	glTexCoord2f(0.0, 0.0);
	glVertex3f(3.0, -2.4, -0.15);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(0, -2.4, -0.15);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(0, -2.4, 0.0);

	glTexCoord2f(0.0, 0.0);
	glVertex3f(0, 0, 0.0); //Orta üst
	glTexCoord2f(0.0, 0.0);
	glVertex3f(3.0, 0, 0.0);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(3.0, 0, -0.15);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(0, 0, -0.15);

	glTexCoord2f(0.0, 0.0);
	glVertex3f(3.0, 0, 0.0); //Orta sağ
	glTexCoord2f(0.0, 0.0);
	glVertex3f(3.0, 0, -0.15);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(3.0, -2.4, 0.0);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(3.0, -2.4, -0.15);

	glTexCoord2f(0.0, 0.0);
	glVertex3f(0, -2.4, 0.0); //orta sol
	glTexCoord2f(0.0, 0.0);
	glVertex3f(0, -2.4, -0.15);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(0, 0, 0.0);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(0, 0, -0.15);

	glTexCoord2f(0.0, 0.0);
	glVertex3f(0, -2.4, -0.15);// temas eden alttaki
	glTexCoord2f(0.0, 0.0);
	glVertex3f(0, 0, -0.15);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(3.0, 0, -0.15);
	glTexCoord2f(0.0, 1.0);
	glVertex3f(3.0, -2.4, -0.15);

	glEnd();
	glPopMatrix();
	glDisable(GL_TEXTURE_2D);

	/*
	 Game::ChosenChessManCoordinate.x = 7;
	 Game::ChosenChessManCoordinate.y = 2;

	 VisitableCoordinate vCoord;
	 vCoord.coordType = ChessManVisitableCoordinateType_Attack;
	 vCoord.ctCoord.x = 0;
	 vCoord.ctCoord.y = 0;
	 ChessTable::MovementList.push_back(vCoord);
	 */

	//////DOSYA ISLEMI
	if (Game::chessPlayerTurn == ChessPlayerTurn_White) // sıra beyazdaysa calısmalı
	{
		ChessCommand cCommand;
		if (sf.OpenFile()) {
			if (sf.GetSize() == sf.size) {

				//burda	bisey yapma cunku yeni komut gelmemis
				//sndSource.PlayCheckMateWin();
			} else {
				cCommand = sf.GetCommand();
				if (!cCommand.isCommandOK) {
					// bise yapma cunku komut bozuk
				} else {
					switch (cCommand.ccType) {
					case ChessCommandType_Select: {
						ChessTable::selectedCoordinate.x = cCommand.cclType;
						ChessTable::selectedCoordinate.y = cCommand.ccdType;
						if (ChessTable::ChessTableMatrix[cCommand.cclType][cCommand.ccdType].color
								== ChessManColor_White) {
							ct.GetMovementList(ChessTable::selectedCoordinate,
									ChessTable::ChessTableMatrix);
							ct.RefineMovementList(
									ChessTable::selectedCoordinate,
									ChessTable::ChessTableMatrix);
							ChessTable::selectedCoordinateMovementList
									= ChessTable::MovementList;
							if (!ChessTable::selectedCoordinateMovementList.empty()) {
								SpeechFile::speechSituation
										= SpeechSituation_WaitingForMove; // move a cevrilir
							} else {
								sndSource.PlayWrongChessManSelection();
							}
						} else {
							sndSource.PlayWrongChessManSelection();
						}
					}
						break;
					case ChessCommandType_Move: {
						ChessTableCoordinate newCoord;
						newCoord.x = cCommand.cclType;
						newCoord.y = cCommand.ccdType;
						// chesstable ın son hali undo matrix e kopyalanmalı
						ct.EqualTwoArrays(ChessTable::ChessTableMatrix,
								ChessTable::UndoMatrix);
						// son yenilen tas init edilmeli
						ChessTable::LastEatenBlackChessMan = ChessMan();
						ChessTableMtrx chTbMtrx;
						ChessTable::MovementList
								= ChessTable::selectedCoordinateMovementList;
						chTbMtrx = ct.MakeMovement(
								ChessTable::selectedCoordinate, newCoord,
								ChessTable::ChessTableMatrix, true);
						if (chTbMtrx.isChanged) {
							ct.EqualTwoArrays(chTbMtrx.data,
									ChessTable::ChessTableMatrix);
							ChessTable::selectedCoordinate.x = -100;
							ChessTable::selectedCoordinate.y = -100;
							Game::chessPlayerTurn = ChessPlayerTurn_Black; // sıra siyaha verilir
							ChessTable::selectedCoordinateMovementList.erase(
									ChessTable::selectedCoordinateMovementList.begin(),
									ChessTable::selectedCoordinateMovementList.end());
							SpeechFile::speechSituation
									= SpeechSituation_WaitingForSelect; // select e cevrilir
						} else {
							sndSource.PlayWrongChessManSelection();
						}
					}
						break;
					case ChessCommandType_Cancel:
						ChessTable::selectedCoordinate.x = -100;
						ChessTable::selectedCoordinate.y = -100;
						// selected movement list bosaltılmalı
						ChessTable::selectedCoordinateMovementList.erase(
								ChessTable::selectedCoordinateMovementList.begin(),
								ChessTable::selectedCoordinateMovementList.end());
						SpeechFile::speechSituation
								= SpeechSituation_WaitingForSelect; // select e cevrilir
						break;
					case ChessCommandType_Undo:
						ct.EqualTwoArrays(ChessTable::UndoMatrix,
								ChessTable::ChessTableMatrix);
						ct.CheckForBeatenChessMan();
						break;
					default:
						break;
					}
				}
			}
			sf.CloseFile();
		}
		if (!ChessTable::selectedCoordinateMovementList.empty()) {
			/// durum select bekleniyor a alınabilir.
			so.ShowPossibleMovementsOfSelectedChessMan();
		}
	} else if (Game::chessPlayerTurn == ChessPlayerTurn_Black) {
		//glRotatef(-180.0f, 0.0f, 0.0f, 1.0f);
		if (aiCt.GetBestValueOfMinMaxTree(ChessTable::ChessTableMatrix,
				ChessManColor_Black)) {
			Game::chessPlayerTurn = ChessPlayerTurn_White;
		}
	}

	so.DetermineChessMenFiguresOnChessTable();
	//taslar bulunduklari yerlere oturtulur.
	so.ShowGameMessage();

	glutSwapBuffers(); //glFlush(); // kapalı da kalabilir
	ct.IsCheckMateOrCheck(ChessTable::ChessTableMatrix); // check mate kontrol edilir

	alureSleep(0.5); // calisiyor

	glutPostRedisplay();
}

void reshape(int w, int h) {
	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, (GLfloat) w / (GLfloat) h, 1.0, 30.0); // sinan
	//gluPerspective(90.0, (GLfloat) w / (GLfloat) h, 1.0, 30.0); // ceren
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0.0, 0.0, -2.7);

}

/*
 void keyboard(unsigned char key, int x, int y) {
 /* sleep to avoid thrashing this procedure

 //usleep(100);

 if (key == ESCAPE) {
 /* shut down our window */
//		glutDestroyWindow(window);

/* exit the program...normal termination.
 exit(0);
 }
 switch (key) {
 case 's':
 break;

 case 'g':
 break;

 default:
 break;
 }
 glutPostRedisplay();

 }
 */

void processSpecialKeys(int key, int x, int y) {

	int mod;
	switch (key) {
	case GLUT_KEY_F3:
		mod = glutGetModifiers();
		if (mod == (GLUT_ACTIVE_ALT)) {

			int x;
			pthread_setcancelstate(PTHREAD_CANCEL_ASYNCHRONOUS, &x);
			pthread_cancel(Game::HTKJuliusThread);
			exit(0);

		}
	}
}

void* makeTerminalExecute(void* context) {

	putenv("PATH=/home/sinan/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/sinan/bin/julius-3.5.2-multipath-linuxbin/bin:/home/sinan/bin/htk/bin:");

//	char * pPath;
//	pPath = getenv("PATH");
//	printf("The current path is: %s", pPath);

//	system("cd ../../bin/SpeechTwo/manual && padsp julian -input mic -C julian.jconf > recognitionOutput");

	system("cd src/SpeechRecognition && padsp julian -input mic -C julian.jconf > recognitionOutput");

}

typedef struct _arguments {
	int *argc;
	char **argv;
} Arguments;

void* makeGlutLoop(void* context) {

	Arguments *arg = (Arguments*) context;
	glutInit(arg->argc, arg->argv);
	// ALUT
	alutInit(arg->argc, arg->argv);
	SoundSource ss;
	ss.InitAllWarningSounds();
	// ALUT
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
	//glutInitWindowSize(800, 600);
	//glutInitWindowPosition(150, 0);
	glutCreateWindow("3D Voice Controlled Chess");
	init();
	glutFullScreen();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	//glutKeyboardFunc(keyboard);
	glutSpecialFunc(processSpecialKeys);
	glutMainLoop();
}

int main(int argc, char** argv) {

//	pthread_t thread1, thread2;

	Arguments arg;
	arg.argc = &argc;
	arg.argv = argv;

	pthread_create(&Game::OpenGLThread, NULL, makeGlutLoop, &arg);
	pthread_create(&Game::HTKJuliusThread, NULL, makeTerminalExecute, NULL);

	pthread_join(Game::OpenGLThread, NULL);
	pthread_join(Game::HTKJuliusThread, NULL);

	//pthread_exit(NULL);

	return 0;
}

